// Copyright 2016 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <hilog/log.h>
#include <cassert>
#include <vector>
#include <cmath>

#include <native_window/external_window.h>

#include <stb/stb_image.h>
#include "CreateShaderModule.h"
#include "VulkanMain.hpp"

#include <vulkan/vulkan.h>
#include <rawfile/raw_file_manager.h>
#include <rawfile/raw_file.h>
#include <ace/xcomponent/native_interface_xcomponent.h>

// HarmonyOS Vulkan 扩展兼容性定义
#ifndef VK_KHR_OHOS_SURFACE_EXTENSION_NAME
#define VK_KHR_OHOS_SURFACE_EXTENSION_NAME "VK_KHR_ohos_surface"
#endif

#ifndef VK_STRUCTURE_TYPE_OHOS_SURFACE_CREATE_INFO_KHR
#define VK_STRUCTURE_TYPE_OHOS_SURFACE_CREATE_INFO_KHR ((VkStructureType)1000282000)
#endif

// Android 兼容性结构体定义
typedef struct VkAndroidSurfaceCreateInfoKHR {
  VkStructureType    sType;
  const void*        pNext;
  VkFlags            flags;
  struct ANativeWindow* window;
} VkAndroidSurfaceCreateInfoKHR;

// ANativeWindow 类型（用于兼容）
typedef struct ANativeWindow ANativeWindow;

// OHOS Surface 结构体
typedef struct VkOhosSurfaceCreateInfoKHR {
  VkStructureType    sType;
  const void*        pNext;
  VkFlags            flags;
  OHNativeWindow*    window;  // 修复：移除 struct 关键字
} VkOhosSurfaceCreateInfoKHR;

// 函数指针定义
typedef VkResult (VKAPI_PTR *PFN_vkCreateOhosSurfaceKHR)(
  VkInstance                        instance,
  const VkOhosSurfaceCreateInfoKHR* pCreateInfo,
  const VkAllocationCallbacks*      pAllocator,
  VkSurfaceKHR*                     pSurface);

// Android 兼容性函数指针定义
typedef VkResult (VKAPI_PTR *PFN_vkCreateAndroidSurfaceKHR)(
  VkInstance                           instance,
  const VkAndroidSurfaceCreateInfoKHR* pCreateInfo,  // 修复：使用正确的类型
  const VkAllocationCallbacks*         pAllocator,
  VkSurfaceKHR*                        pSurface);

// 全局函数指针
static PFN_vkCreateOhosSurfaceKHR vkCreateOhosSurfaceKHR = nullptr;

// HarmonyOS 日志宏定义
#undef LOG_DOMAIN
#undef LOG_TAG
#define LOG_DOMAIN 0x3200
#define LOG_TAG "VulkanHarmony"

#define LOGI(...) OH_LOG_Print(LOG_APP, LOG_INFO, LOG_DOMAIN, LOG_TAG, __VA_ARGS__)
#define LOGW(...) OH_LOG_Print(LOG_APP, LOG_WARN, LOG_DOMAIN, LOG_TAG, __VA_ARGS__)
#define LOGE(...) OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_DOMAIN, LOG_TAG, __VA_ARGS__)

// Vulkan call wrapper
#define CALL_VK(func)                                                 \
  if (VK_SUCCESS != (func)) {                                         \
    LOGE("Vulkan error. File[%s], line[%d]", __FILE__, __LINE__);      \
    assert(false);                                                    \
  }

#define VK_CHECK(x) CALL_VK(x)

// Global Variables ...

VulkanDeviceInfo device;

struct VulkanSwapchainInfo {
  VkSwapchainKHR swapchain_;
  uint32_t swapchainLength_;

  VkExtent2D displaySize_;
  VkFormat displayFormat_;

  // array of frame buffers and views
  VkFramebuffer* framebuffers_;
  VkImage* displayImages_;
  VkImageView* displayViews_;
};
VulkanSwapchainInfo swapchain;

typedef struct texture_object {
  VkSampler sampler;
  VkImage image;
  VkImageLayout imageLayout;
  VkDeviceMemory mem;
  VkImageView view;
  int32_t tex_width;
  int32_t tex_height;
} texture_object;
static const VkFormat kTexFmt = VK_FORMAT_R8G8B8A8_UNORM;
#define TUTORIAL_TEXTURE_COUNT 1
const char* texFiles[TUTORIAL_TEXTURE_COUNT] = {
    "sample_tex_3.png",
};
struct texture_object textures[TUTORIAL_TEXTURE_COUNT];

struct VulkanBufferInfo {
  VkBuffer vertexBuf_;
  VkDeviceMemory memory_;
  void* mappedData_;  // 用于动态更新顶点数据
};

VulkanBufferInfo buffers;

struct VulkanGfxPipelineInfo {
  VkDescriptorSetLayout dscLayout_;
  VkDescriptorPool descPool_;
  VkDescriptorSet descSet_;
  VkPipelineLayout layout_;
  VkPipelineCache cache_;
  VkPipeline pipeline_;
};
VulkanGfxPipelineInfo gfxPipeline;

struct VulkanRenderInfo {
  VkRenderPass renderPass_;
  VkCommandPool cmdPool_;
  VkCommandBuffer* cmdBuffer_;
  uint32_t cmdBufferLen_;
  VkSemaphore semaphore_;
  VkFence fence_;
};
VulkanRenderInfo render;

OHNativeWindow* g_nativeWindow = nullptr;
bool g_surfaceReady = false;
uint64_t g_windowWidth = 0;
uint64_t g_windowHeight = 0;


// HarmonyOS Native App pointer...
OH_NativeXComponent* nativeXComponent = nullptr;
NativeResourceManager* resourceManager = nullptr;

void setImageLayout(VkCommandBuffer cmdBuffer, VkImage image,
                    VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
                    VkPipelineStageFlags srcStages,
                    VkPipelineStageFlags destStages);

// Create vulkan device
void CreateVulkanDevice(OHNativeWindow* platformWindow,
                        VkApplicationInfo* appInfo) {
  std::vector<const char*> instance_extensions;
  std::vector<const char*> device_extensions;

  instance_extensions.push_back("VK_KHR_surface");
  // 使用兼容性扩展名
  instance_extensions.push_back(VK_KHR_OHOS_SURFACE_EXTENSION_NAME);

  device_extensions.push_back("VK_KHR_swapchain");

  // **********************************************************
  // Create the Vulkan instance
  VkInstanceCreateInfo instanceCreateInfo{
      .sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
      .pNext = nullptr,
      .pApplicationInfo = appInfo,
      .enabledLayerCount = 0,
      .ppEnabledLayerNames = nullptr,
      .enabledExtensionCount =
          static_cast<uint32_t>(instance_extensions.size()),
      .ppEnabledExtensionNames = instance_extensions.data(),
  };
  CALL_VK(vkCreateInstance(&instanceCreateInfo, nullptr, &device.instance_));
  
  // 获取 OHOS surface 创建函数指针
  vkCreateOhosSurfaceKHR = (PFN_vkCreateOhosSurfaceKHR)
      vkGetInstanceProcAddr(device.instance_, "vkCreateOhosSurfaceKHR");
  
  if (!vkCreateOhosSurfaceKHR) {
      LOGW("vkCreateOhosSurfaceKHR not available, trying Android compatibility");
      // 尝试使用 Android 兼容模式
      PFN_vkCreateAndroidSurfaceKHR vkCreateAndroidSurfaceKHR = 
          (PFN_vkCreateAndroidSurfaceKHR)vkGetInstanceProcAddr(device.instance_, "vkCreateAndroidSurfaceKHR");
      
      if (vkCreateAndroidSurfaceKHR) {
          vkCreateOhosSurfaceKHR = (PFN_vkCreateOhosSurfaceKHR)vkCreateAndroidSurfaceKHR;
          LOGI("Using Android surface compatibility mode");
      } else {
          LOGE("No compatible surface extension found");
          assert(false);
      }
  }
  
  // Create OHOS Surface
  VkOhosSurfaceCreateInfoKHR createInfo{
      .sType = VK_STRUCTURE_TYPE_OHOS_SURFACE_CREATE_INFO_KHR,
      .pNext = nullptr,
      .flags = 0,
      .window = platformWindow
  };

  CALL_VK(vkCreateOhosSurfaceKHR(device.instance_, &createInfo, nullptr,
                                    &device.surface_));
  // Find one GPU to use:
  uint32_t gpuCount = 0;
  CALL_VK(vkEnumeratePhysicalDevices(device.instance_, &gpuCount, nullptr));
  VkPhysicalDevice tmpGpus[gpuCount];
  CALL_VK(vkEnumeratePhysicalDevices(device.instance_, &gpuCount, tmpGpus));
  device.gpuDevice_ = tmpGpus[0];  // Pick up the first GPU Device

  vkGetPhysicalDeviceMemoryProperties(device.gpuDevice_,
                                      &device.gpuMemoryProperties_);

  // Find a GFX queue family
  uint32_t queueFamilyCount;
  vkGetPhysicalDeviceQueueFamilyProperties(device.gpuDevice_, &queueFamilyCount,
                                           nullptr);
  assert(queueFamilyCount);
  std::vector<VkQueueFamilyProperties> queueFamilyProperties(queueFamilyCount);
  vkGetPhysicalDeviceQueueFamilyProperties(device.gpuDevice_, &queueFamilyCount,
                                           queueFamilyProperties.data());

  uint32_t queueFamilyIndex;
  for (queueFamilyIndex = 0; queueFamilyIndex < queueFamilyCount;
       queueFamilyIndex++) {
    if (queueFamilyProperties[queueFamilyIndex].queueFlags &
        VK_QUEUE_GRAPHICS_BIT) {
      break;
    }
  }
  assert(queueFamilyIndex < queueFamilyCount);
  device.queueFamilyIndex_ = queueFamilyIndex;
  // Create a logical device (vulkan device)
  float priorities[] = {
      1.0f,
  };
  VkDeviceQueueCreateInfo queueCreateInfo{
      .sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
      .queueFamilyIndex = device.queueFamilyIndex_,
      .queueCount = 1,
      .pQueuePriorities = priorities,
  };

  VkDeviceCreateInfo deviceCreateInfo{
      .sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
      .pNext = nullptr,
      .queueCreateInfoCount = 1,
      .pQueueCreateInfos = &queueCreateInfo,
      .enabledLayerCount = 0,
      .ppEnabledLayerNames = nullptr,
      .enabledExtensionCount = static_cast<uint32_t>(device_extensions.size()),
      .ppEnabledExtensionNames = device_extensions.data(),
      .pEnabledFeatures = nullptr,
  };

  CALL_VK(vkCreateDevice(device.gpuDevice_, &deviceCreateInfo, nullptr,
                         &device.device_));
  vkGetDeviceQueue(device.device_, 0, 0, &device.queue_);
}

void CreateSwapChain(void) {
  LOGI("->createSwapChain");
  memset(&swapchain, 0, sizeof(swapchain));

  // **********************************************************
  // Get the surface capabilities because:
  //   - It contains the minimal and max length of the chain, we will need it
  //   - It's necessary to query the supported surface format (R8G8B8A8 for
  //   instance ...)
  VkSurfaceCapabilitiesKHR surfaceCapabilities;
  vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device.gpuDevice_, device.surface_,
                                            &surfaceCapabilities);
  // Query the list of supported surface format and choose one we like
  uint32_t formatCount = 0;
  vkGetPhysicalDeviceSurfaceFormatsKHR(device.gpuDevice_, device.surface_,
                                       &formatCount, nullptr);
  VkSurfaceFormatKHR* formats = new VkSurfaceFormatKHR[formatCount];
  vkGetPhysicalDeviceSurfaceFormatsKHR(device.gpuDevice_, device.surface_,
                                       &formatCount, formats);
  LOGI("Got %d formats", formatCount);

  uint32_t chosenFormat;
  for (chosenFormat = 0; chosenFormat < formatCount; chosenFormat++) {
    if (formats[chosenFormat].format == VK_FORMAT_R8G8B8A8_UNORM) break;
  }
  assert(chosenFormat < formatCount);

  swapchain.displaySize_ = surfaceCapabilities.currentExtent;
  swapchain.displayFormat_ = formats[chosenFormat].format;

  // **********************************************************
  // Create a swap chain (here we choose the minimum available number of surface
  // in the chain)
  VkSwapchainCreateInfoKHR swapchainCreateInfo{
      .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
      .pNext = nullptr,
      .surface = device.surface_,
      .minImageCount = surfaceCapabilities.minImageCount,
      .imageFormat = formats[chosenFormat].format,
      .imageColorSpace = formats[chosenFormat].colorSpace,
      .imageExtent = surfaceCapabilities.currentExtent,
      .imageArrayLayers = 1,
      .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
      .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .queueFamilyIndexCount = 1,
      .pQueueFamilyIndices = &device.queueFamilyIndex_,
      .preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
      .compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
      .presentMode = VK_PRESENT_MODE_FIFO_KHR,
      .clipped = VK_FALSE,
      .oldSwapchain = VK_NULL_HANDLE,
  };
  CALL_VK(vkCreateSwapchainKHR(device.device_, &swapchainCreateInfo, nullptr,
                               &swapchain.swapchain_));

  // Get the length of the created swap chain
  CALL_VK(vkGetSwapchainImagesKHR(device.device_, swapchain.swapchain_,
                                  &swapchain.swapchainLength_, nullptr));
  delete[] formats;
  LOGI("<-createSwapChain");
}


void CreateFrameBuffers(VkRenderPass& renderPass,
                        VkImageView depthView = VK_NULL_HANDLE) {
  // query display attachment to swapchain
  uint32_t SwapchainImagesCount = 0;
  CALL_VK(vkGetSwapchainImagesKHR(device.device_, swapchain.swapchain_,
                                  &SwapchainImagesCount, nullptr));
  swapchain.displayImages_ = new VkImage[SwapchainImagesCount];
  CALL_VK(vkGetSwapchainImagesKHR(device.device_, swapchain.swapchain_,
                                  &SwapchainImagesCount,
                                  swapchain.displayImages_));

  // create image view for each swapchain image
  swapchain.displayViews_ = new VkImageView[SwapchainImagesCount];
  for (uint32_t i = 0; i < SwapchainImagesCount; i++) {
    VkImageViewCreateInfo viewCreateInfo = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
        .pNext = nullptr,
        .flags = 0,
        .image = swapchain.displayImages_[i],
        .viewType = VK_IMAGE_VIEW_TYPE_2D,
        .format = swapchain.displayFormat_,
        .components =
            {
                .r = VK_COMPONENT_SWIZZLE_R,
                .g = VK_COMPONENT_SWIZZLE_G,
                .b = VK_COMPONENT_SWIZZLE_B,
                .a = VK_COMPONENT_SWIZZLE_A,
            },
        .subresourceRange =
            {
                .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
                .baseMipLevel = 0,
                .levelCount = 1,
                .baseArrayLayer = 0,
                .layerCount = 1,
            },
    };
    CALL_VK(vkCreateImageView(device.device_, &viewCreateInfo, nullptr,
                              &swapchain.displayViews_[i]));
  }

  // create a framebuffer from each swapchain image
  swapchain.framebuffers_ = new VkFramebuffer[swapchain.swapchainLength_];
  for (uint32_t i = 0; i < swapchain.swapchainLength_; i++) {
    VkImageView attachments[2] = {
        swapchain.displayViews_[i], depthView,
    };
    VkFramebufferCreateInfo fbCreateInfo{
        .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
        .pNext = nullptr,
        .renderPass = renderPass,
        .attachmentCount = 1,  // 2 if using depth
        .pAttachments = attachments,
        .width = static_cast<uint32_t>(swapchain.displaySize_.width),
        .height = static_cast<uint32_t>(swapchain.displaySize_.height),
        .layers = 1,
    };
    fbCreateInfo.attachmentCount = (depthView == VK_NULL_HANDLE ? 1 : 2);

    CALL_VK(vkCreateFramebuffer(device.device_, &fbCreateInfo, nullptr,
                                &swapchain.framebuffers_[i]));
  }
}

// A help function to map required memory property into a VK memory type
// memory type is an index into the array of 32 entries; or the bit index
// for the memory type ( each BIT of an 32 bit integer is a type ).
VkResult AllocateMemoryTypeFromProperties(uint32_t typeBits,
                                          VkFlags requirements_mask,
                                          uint32_t* typeIndex) {
  // Search memtypes to find first index with those properties
  for (uint32_t i = 0; i < 32; i++) {
    if ((typeBits & 1) == 1) {
      // Type is available, does it match user properties?
      if ((device.gpuMemoryProperties_.memoryTypes[i].propertyFlags &
           requirements_mask) == requirements_mask) {
        *typeIndex = i;
        return VK_SUCCESS;
      }
    }
    typeBits >>= 1;
  }
  // No memory types matched, return failure
  return VK_ERROR_MEMORY_MAP_FAILED;
}


VkResult LoadTextureFromFile(const char* filePath,
                             struct texture_object* tex_obj,
                             VkImageUsageFlags usage, VkFlags required_props) {
  if (!(usage | required_props)) {
    LOGE("No usage and required_pros for texture loading");
    return VK_ERROR_FORMAT_NOT_SUPPORTED;
  }

  // Check for linear supportability
  VkFormatProperties props;
  bool needBlit = true;
  vkGetPhysicalDeviceFormatProperties(device.gpuDevice_, kTexFmt, &props);
  assert((props.linearTilingFeatures | props.optimalTilingFeatures) &
         VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT);

  if (props.linearTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT) {
    // linear format supporting the required texture
    needBlit = false;
  }

  // Read the file from HarmonyOS resources:
  if (!resourceManager) {
      LOGE("ResourceManager is null");
      return VK_ERROR_INITIALIZATION_FAILED;
  }
  RawFile* rawFile = OH_ResourceManager_OpenRawFile(resourceManager, filePath);
  if (!rawFile) {
      LOGE("Failed to open raw file: %s", filePath);
      return VK_ERROR_INITIALIZATION_FAILED;
  }
  long fileLength = OH_ResourceManager_GetRawFileSize(rawFile);
  stbi_uc* fileContent = new stbi_uc[fileLength];
  int readResult = OH_ResourceManager_ReadRawFile(rawFile, fileContent, fileLength);
  OH_ResourceManager_CloseRawFile(rawFile);
  if (readResult < 0) {
      LOGE("Failed to read raw file: %s", filePath);
      delete[] fileContent;
      return VK_ERROR_INITIALIZATION_FAILED;
  }

  uint32_t imgWidth, imgHeight, n;
  unsigned char* imageData = stbi_load_from_memory(
      fileContent, fileLength, reinterpret_cast<int*>(&imgWidth),
      reinterpret_cast<int*>(&imgHeight), reinterpret_cast<int*>(&n), 4);
  
  if (n != 4) {
    // 如果不是 RGBA 格式，手动转换
    unsigned char* rgbaData = new unsigned char[imgWidth * imgHeight * 4];
    for (uint32_t i = 0; i < imgWidth * imgHeight; i++) {
        rgbaData[i * 4 + 0] = imageData[i * n + 0];  // R
        rgbaData[i * 4 + 1] = imageData[i * n + 1];  // G
        rgbaData[i * 4 + 2] = imageData[i * n + 2];  // B
        rgbaData[i * 4 + 3] = (n == 3) ? 255 : imageData[i * n + 3];  // A
    }
    stbi_image_free(imageData);  // 释放原始数据
    imageData = rgbaData;        // 替换为 RGBA 数据
    n = 4;                       // 更新通道数
  }  
  assert(n == 4);

  tex_obj->tex_width = imgWidth;
  tex_obj->tex_height = imgHeight;

  // Allocate the linear texture so texture could be copied over
  VkImageCreateInfo image_create_info = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
      .imageType = VK_IMAGE_TYPE_2D,
      .format = kTexFmt,
      .extent = {static_cast<uint32_t>(imgWidth),
                 static_cast<uint32_t>(imgHeight), 1},
      .mipLevels = 1,
      .arrayLayers = 1,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .tiling = VK_IMAGE_TILING_LINEAR,
      .usage = (needBlit ? VK_IMAGE_USAGE_TRANSFER_SRC_BIT
                         : VK_IMAGE_USAGE_SAMPLED_BIT),
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .queueFamilyIndexCount = 1,
      .pQueueFamilyIndices = &device.queueFamilyIndex_,
      .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
  };
  VkMemoryAllocateInfo mem_alloc = {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .pNext = nullptr,
      .allocationSize = 0,
      .memoryTypeIndex = 0,
  };

  VkMemoryRequirements mem_reqs;
  CALL_VK(vkCreateImage(device.device_, &image_create_info, nullptr,
                        &tex_obj->image));
  vkGetImageMemoryRequirements(device.device_, tex_obj->image, &mem_reqs);
  mem_alloc.allocationSize = mem_reqs.size;
  VK_CHECK(AllocateMemoryTypeFromProperties(mem_reqs.memoryTypeBits,
                                            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
                                            &mem_alloc.memoryTypeIndex));
  CALL_VK(vkAllocateMemory(device.device_, &mem_alloc, nullptr, &tex_obj->mem));
  CALL_VK(vkBindImageMemory(device.device_, tex_obj->image, tex_obj->mem, 0));

  if (required_props & VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT) {
    const VkImageSubresource subres = {
        .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .mipLevel = 0, .arrayLayer = 0,
    };
    VkSubresourceLayout layout;
    void* data;

    vkGetImageSubresourceLayout(device.device_, tex_obj->image, &subres,
                                &layout);
    CALL_VK(vkMapMemory(device.device_, tex_obj->mem, 0,
                        mem_alloc.allocationSize, 0, &data));

    for (int32_t y = 0; y < imgHeight; y++) {
      unsigned char* row = (unsigned char*)((char*)data + layout.rowPitch * y);
      for (int32_t x = 0; x < imgWidth; x++) {
        row[x * 4] = imageData[(x + y * imgWidth) * 4];
        row[x * 4 + 1] = imageData[(x + y * imgWidth) * 4 + 1];
        row[x * 4 + 2] = imageData[(x + y * imgWidth) * 4 + 2];
        row[x * 4 + 3] = imageData[(x + y * imgWidth) * 4 + 3];
      }
    }

    vkUnmapMemory(device.device_, tex_obj->mem);
    stbi_image_free(imageData);
  }
  delete[] fileContent;

  tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;

  VkCommandPoolCreateInfo cmdPoolCreateInfo{
      .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
      .pNext = nullptr,
      .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
      .queueFamilyIndex = device.queueFamilyIndex_,
  };

  VkCommandPool cmdPool;
  CALL_VK(vkCreateCommandPool(device.device_, &cmdPoolCreateInfo, nullptr,
                              &cmdPool));

  VkCommandBuffer gfxCmd;
  const VkCommandBufferAllocateInfo cmd = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      .pNext = nullptr,
      .commandPool = cmdPool,
      .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      .commandBufferCount = 1,
  };

  CALL_VK(vkAllocateCommandBuffers(device.device_, &cmd, &gfxCmd));
  VkCommandBufferBeginInfo cmd_buf_info = {
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
      .pNext = nullptr,
      .flags = 0,
      .pInheritanceInfo = nullptr};
  CALL_VK(vkBeginCommandBuffer(gfxCmd, &cmd_buf_info));

  // If linear is supported, we are done
  VkImage stageImage = VK_NULL_HANDLE;
  VkDeviceMemory stageMem = VK_NULL_HANDLE;
  if (!needBlit) {
    setImageLayout(gfxCmd, tex_obj->image, VK_IMAGE_LAYOUT_PREINITIALIZED,
                   VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
                   VK_PIPELINE_STAGE_HOST_BIT,
                   VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
  } else {
    // save current image and mem as staging image and memory
    stageImage = tex_obj->image;
    stageMem = tex_obj->mem;
    tex_obj->image = VK_NULL_HANDLE;
    tex_obj->mem = VK_NULL_HANDLE;

    // Create a tile texture to blit into
    image_create_info.tiling = VK_IMAGE_TILING_OPTIMAL;
    image_create_info.usage =
        VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_SAMPLED_BIT;
    image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
    CALL_VK(vkCreateImage(device.device_, &image_create_info, nullptr,
                          &tex_obj->image));
    vkGetImageMemoryRequirements(device.device_, tex_obj->image, &mem_reqs);

    mem_alloc.allocationSize = mem_reqs.size;
    VK_CHECK(AllocateMemoryTypeFromProperties(
        mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
        &mem_alloc.memoryTypeIndex));
    CALL_VK(
        vkAllocateMemory(device.device_, &mem_alloc, nullptr, &tex_obj->mem));
    CALL_VK(vkBindImageMemory(device.device_, tex_obj->image, tex_obj->mem, 0));

    // transitions image out of UNDEFINED type
    setImageLayout(gfxCmd, stageImage, VK_IMAGE_LAYOUT_PREINITIALIZED,
                   VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
                   VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
    setImageLayout(gfxCmd, tex_obj->image, VK_IMAGE_LAYOUT_UNDEFINED,
                   VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                   VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
    VkImageCopy bltInfo{
        .srcSubresource {
            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
            .mipLevel = 0,
            .baseArrayLayer = 0,
            .layerCount = 1,
         },
        .srcOffset { .x = 0, .y = 0, .z = 0 },
        .dstSubresource {
            .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
            .mipLevel = 0,
            .baseArrayLayer = 0,
            .layerCount = 1,
         },
        .dstOffset { .x = 0, .y = 0, .z = 0},
        .extent { .width = imgWidth, .height = imgHeight, .depth = 1,},
    };
    vkCmdCopyImage(gfxCmd, stageImage, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
                   tex_obj->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1,
                   &bltInfo);

    setImageLayout(gfxCmd, tex_obj->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
                   VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
                   VK_PIPELINE_STAGE_TRANSFER_BIT,
                   VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT);
  }

  CALL_VK(vkEndCommandBuffer(gfxCmd));
  VkFenceCreateInfo fenceInfo = {
      .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
  };
  VkFence fence;
  CALL_VK(vkCreateFence(device.device_, &fenceInfo, nullptr, &fence));

  VkSubmitInfo submitInfo = {
      .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
      .pNext = nullptr,
      .waitSemaphoreCount = 0,
      .pWaitSemaphores = nullptr,
      .pWaitDstStageMask = nullptr,
      .commandBufferCount = 1,
      .pCommandBuffers = &gfxCmd,
      .signalSemaphoreCount = 0,
      .pSignalSemaphores = nullptr,
  };
  CALL_VK(vkQueueSubmit(device.queue_, 1, &submitInfo, fence));
  CALL_VK(vkWaitForFences(device.device_, 1, &fence, VK_TRUE, 100000000));
  vkDestroyFence(device.device_, fence, nullptr);

  vkFreeCommandBuffers(device.device_, cmdPool, 1, &gfxCmd);
  vkDestroyCommandPool(device.device_, cmdPool, nullptr);
  if (stageImage != VK_NULL_HANDLE) {
    vkDestroyImage(device.device_, stageImage, nullptr);
    vkFreeMemory(device.device_, stageMem, nullptr);
  }
  return VK_SUCCESS;
}


void CreateDummyTexture(struct texture_object* tex_obj) {
  LOGI("Creating dummy texture");
  
  // 创建一个简单的 4x4 纯色纹理
  const uint32_t imgWidth = 4;
  const uint32_t imgHeight = 4;
  const uint32_t pixelSize = 4; // RGBA
  
  // 创建纯红色纹理数据
  uint8_t textureData[imgWidth * imgHeight * pixelSize];
  for (uint32_t i = 0; i < imgWidth * imgHeight; i++) {
      textureData[i * 4 + 0] = 255; // R
      textureData[i * 4 + 1] = 0;   // G
      textureData[i * 4 + 2] = 0;   // B
      textureData[i * 4 + 3] = 255; // A
  }
  
  tex_obj->tex_width = imgWidth;
  tex_obj->tex_height = imgHeight;

  // 创建图像
  VkImageCreateInfo image_create_info = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
      .imageType = VK_IMAGE_TYPE_2D,
      .format = kTexFmt,
      .extent = {imgWidth, imgHeight, 1},
      .mipLevels = 1,
      .arrayLayers = 1,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .tiling = VK_IMAGE_TILING_LINEAR,
      .usage = VK_IMAGE_USAGE_SAMPLED_BIT,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .queueFamilyIndexCount = 1,
      .pQueueFamilyIndices = &device.queueFamilyIndex_,
      .initialLayout = VK_IMAGE_LAYOUT_PREINITIALIZED,
  };

  VkMemoryAllocateInfo mem_alloc = {
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .pNext = nullptr,
      .allocationSize = 0,
      .memoryTypeIndex = 0,
  };

  VkMemoryRequirements mem_reqs;
  CALL_VK(vkCreateImage(device.device_, &image_create_info, nullptr, &tex_obj->image));
  vkGetImageMemoryRequirements(device.device_, tex_obj->image, &mem_reqs);
  mem_alloc.allocationSize = mem_reqs.size;
  
  VK_CHECK(AllocateMemoryTypeFromProperties(mem_reqs.memoryTypeBits,
                                            VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,
                                            &mem_alloc.memoryTypeIndex));
  CALL_VK(vkAllocateMemory(device.device_, &mem_alloc, nullptr, &tex_obj->mem));
  CALL_VK(vkBindImageMemory(device.device_, tex_obj->image, tex_obj->mem, 0));

  // 映射内存并复制数据
  const VkImageSubresource subres = {
      .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, 
      .mipLevel = 0, 
      .arrayLayer = 0,
  };
  VkSubresourceLayout layout;
  void* data;

  vkGetImageSubresourceLayout(device.device_, tex_obj->image, &subres, &layout);
  CALL_VK(vkMapMemory(device.device_, tex_obj->mem, 0, mem_alloc.allocationSize, 0, &data));

  for (uint32_t y = 0; y < imgHeight; y++) {
      uint8_t* row = (uint8_t*)((char*)data + layout.rowPitch * y);
      for (uint32_t x = 0; x < imgWidth; x++) {
          row[x * 4 + 0] = textureData[(x + y * imgWidth) * 4 + 0];
          row[x * 4 + 1] = textureData[(x + y * imgWidth) * 4 + 1];
          row[x * 4 + 2] = textureData[(x + y * imgWidth) * 4 + 2];
          row[x * 4 + 3] = textureData[(x + y * imgWidth) * 4 + 3];
      }
  }

  vkUnmapMemory(device.device_, tex_obj->mem);
  tex_obj->imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  
  LOGI("Dummy texture created successfully");
}
// void CreateTexture(void) {
//   for (uint32_t i = 0; i < TUTORIAL_TEXTURE_COUNT; i++) {
//     LoadTextureFromFile(texFiles[i], &textures[i], VK_IMAGE_USAGE_SAMPLED_BIT,
//                         VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);

//     const VkSamplerCreateInfo sampler = {
//         .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
//         .pNext = nullptr,
//         .magFilter = VK_FILTER_NEAREST,
//         .minFilter = VK_FILTER_NEAREST,
//         .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
//         .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
//         .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
//         .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
//         .mipLodBias = 0.0f,
//         .maxAnisotropy = 1,
//         .compareOp = VK_COMPARE_OP_NEVER,
//         .minLod = 0.0f,
//         .maxLod = 0.0f,
//         .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
//         .unnormalizedCoordinates = VK_FALSE,
//     };
//     VkImageViewCreateInfo view = {
//         .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
//         .pNext = nullptr,
//         .flags = 0,
//         .image = VK_NULL_HANDLE,
//         .viewType = VK_IMAGE_VIEW_TYPE_2D,
//         .format = kTexFmt,
//         .components =
//             {
//                 VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
//                 VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
//             },
//         .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
//     };

//     CALL_VK(vkCreateSampler(device.device_, &sampler, nullptr,
//                             &textures[i].sampler));
//     view.image = textures[i].image;
//     CALL_VK(
//         vkCreateImageView(device.device_, &view, nullptr, &textures[i].view));
//   }
// }
void CreateTexture(void) {
  // 检查资源管理器是否可用
  if (!resourceManager) {
      LOGW("Resource manager not available, skipping texture loading");
      
      // 创建一个简单的纯色纹理作为替代
      for (uint32_t i = 0; i < TUTORIAL_TEXTURE_COUNT; i++) {
          CreateDummyTexture(&textures[i]);
      }
      return;
  }
  
  // 原有的纹理加载代码
  for (uint32_t i = 0; i < TUTORIAL_TEXTURE_COUNT; i++) {
    VkResult result = LoadTextureFromFile(texFiles[i], &textures[i], VK_IMAGE_USAGE_SAMPLED_BIT,
                        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
    
    if (result != VK_SUCCESS) {
        LOGW("Failed to load texture %s, creating dummy texture", texFiles[i]);
        CreateDummyTexture(&textures[i]);
    }

    const VkSamplerCreateInfo sampler = {
        .sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
        .pNext = nullptr,
        .magFilter = VK_FILTER_NEAREST,
        .minFilter = VK_FILTER_NEAREST,
        .mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST,
        .addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT,
        .addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT,
        .addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT,
        .mipLodBias = 0.0f,
        .maxAnisotropy = 1,
        .compareOp = VK_COMPARE_OP_NEVER,
        .minLod = 0.0f,
        .maxLod = 0.0f,
        .borderColor = VK_BORDER_COLOR_FLOAT_OPAQUE_WHITE,
        .unnormalizedCoordinates = VK_FALSE,
    };
    VkImageViewCreateInfo view = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
        .pNext = nullptr,
        .flags = 0,
        .image = VK_NULL_HANDLE,
        .viewType = VK_IMAGE_VIEW_TYPE_2D,
        .format = kTexFmt,
        .components =
            {
                VK_COMPONENT_SWIZZLE_R, VK_COMPONENT_SWIZZLE_G,
                VK_COMPONENT_SWIZZLE_B, VK_COMPONENT_SWIZZLE_A,
            },
        .subresourceRange = {VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1},
    };

    CALL_VK(vkCreateSampler(device.device_, &sampler, nullptr,
                            &textures[i].sampler));
    view.image = textures[i].image;
    CALL_VK(
        vkCreateImageView(device.device_, &view, nullptr, &textures[i].view));
  }
}

// A helper function
bool MapMemoryTypeToIndex(uint32_t typeBits, VkFlags requirements_mask,
                          uint32_t* typeIndex) {
  VkPhysicalDeviceMemoryProperties memoryProperties;
  vkGetPhysicalDeviceMemoryProperties(device.gpuDevice_, &memoryProperties);
  // Search memtypes to find first index with those properties
  for (uint32_t i = 0; i < 32; i++) {
    if ((typeBits & 1) == 1) {
      // Type is available, does it match user properties?
      if ((memoryProperties.memoryTypes[i].propertyFlags & requirements_mask) ==
          requirements_mask) {
        *typeIndex = i;
        return true;
      }
    }
    typeBits >>= 1;
  }
  return false;
}

// 从设备读取数据
void InvalidateBufferData(size_t offset = 0, size_t size = VK_WHOLE_SIZE) {
  VkMappedMemoryRange invalidateRange{
      .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
      .pNext = nullptr,
      .memory = buffers.memory_,
      .offset = offset,
      .size = size
  };
  CALL_VK(vkInvalidateMappedMemoryRanges(device.device_, 1, &invalidateRange));
}

bool CreateBuffers(void) {
  // Vertex positions (initial positions for a triangle)
  const float vertexData[] = {
      -0.5f, -0.5f, 0.0f, 0.0f, 0.0f,  // Vertex 1
       0.5f, -0.5f, 0.0f, 1.0f, 0.0f,  // Vertex 2
       0.0f,  0.5f, 0.0f, 0.5f, 1.0f,  // Vertex 3
  };

  VkBufferCreateInfo createBufferInfo{
      .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
      .size = sizeof(vertexData),
      .usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,
      .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
      .queueFamilyIndexCount = 1,
      .pQueueFamilyIndices = &device.queueFamilyIndex_,
  };

  CALL_VK(vkCreateBuffer(device.device_, &createBufferInfo, nullptr,
                         &buffers.vertexBuf_));

  VkMemoryRequirements memReq;
  vkGetBufferMemoryRequirements(device.device_, buffers.vertexBuf_, &memReq);

  VkMemoryAllocateInfo allocInfo{
      .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
      .pNext = nullptr,
      .allocationSize = memReq.size,
      .memoryTypeIndex = 0,  // Memory type assigned in the next step
  };

  // 修改：移除 HOST_COHERENT 属性
  MapMemoryTypeToIndex(memReq.memoryTypeBits,
    VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT,  // 移除 HOST_COHERENT
    &allocInfo.memoryTypeIndex);

  // Allocate memory for the buffer
  CALL_VK(vkAllocateMemory(device.device_, &allocInfo, nullptr, &buffers.memory_));
  CALL_VK(
      vkBindBufferMemory(device.device_, buffers.vertexBuf_, buffers.memory_, 0));
  
  // 确保映射成功并初始化为 nullptr
  buffers.mappedData_ = nullptr;
  VkResult mapResult = vkMapMemory(device.device_, buffers.memory_, 0, allocInfo.allocationSize, 0, &buffers.mappedData_);
  if (mapResult != VK_SUCCESS) {
    LOGE("Failed to map buffer memory");
    return false;
  }
  // 检查映射是否成功
  if (buffers.mappedData_ == nullptr) {
    LOGE("Buffer mapping returned null pointer");
    return false;
  }
  memcpy(buffers.mappedData_, vertexData, sizeof(vertexData));

  // 添加：手动 flush 到设备
  VkMappedMemoryRange flushRange{
    .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
    .pNext = nullptr,
    .memory = buffers.memory_,
    .offset = 0,
    .size = sizeof(vertexData)  // 使用实际数据大小
  };
  CALL_VK(vkFlushMappedMemoryRanges(device.device_, 1, &flushRange));
  
  
  return true;
}

void DeleteBuffers(void) {
    if (buffers.mappedData_) {
        vkUnmapMemory(device.device_, buffers.memory_);
        buffers.mappedData_ = nullptr;
    }
    if (buffers.vertexBuf_ != VK_NULL_HANDLE) {
        vkDestroyBuffer(device.device_, buffers.vertexBuf_, nullptr);
        buffers.vertexBuf_ = VK_NULL_HANDLE;
    }
    if (buffers.memory_ != VK_NULL_HANDLE) {
        vkFreeMemory(device.device_, buffers.memory_, nullptr);
        buffers.memory_ = VK_NULL_HANDLE;
    }
}

// Create Graphics Pipeline
VkResult CreateGraphicsPipeline(void) {
  memset(&gfxPipeline, 0, sizeof(gfxPipeline));

  const VkDescriptorSetLayoutBinding descriptorSetLayoutBinding{
      .binding = 0,
      .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
      .descriptorCount = TUTORIAL_TEXTURE_COUNT,
      .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT,
      .pImmutableSamplers = nullptr,
  };
  const VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
      .pNext = nullptr,
      .bindingCount = 1,
      .pBindings = &descriptorSetLayoutBinding,
  };
  CALL_VK(vkCreateDescriptorSetLayout(device.device_,
                                      &descriptorSetLayoutCreateInfo, nullptr,
                                      &gfxPipeline.dscLayout_));
  VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
      .pNext = nullptr,
      .setLayoutCount = 1,
      .pSetLayouts = &gfxPipeline.dscLayout_,
      .pushConstantRangeCount = 0,
      .pPushConstantRanges = nullptr,
  };
  CALL_VK(vkCreatePipelineLayout(device.device_, &pipelineLayoutCreateInfo,
                                 nullptr, &gfxPipeline.layout_));

  // No dynamic state in that tutorial
  VkPipelineDynamicStateCreateInfo dynamicStateInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
      .pNext = nullptr,
      .dynamicStateCount = 0,
      .pDynamicStates = nullptr};

  VkShaderModule vertexShader, fragmentShader;
  buildShaderFromFile(resourceManager, "shaders/tri.vert",
                      VK_SHADER_STAGE_VERTEX_BIT, device.device_,
                      &vertexShader);
  buildShaderFromFile(resourceManager, "shaders/tri.frag",
                      VK_SHADER_STAGE_FRAGMENT_BIT, device.device_,
                      &fragmentShader);
  // Specify vertex and fragment shader stages
  VkPipelineShaderStageCreateInfo shaderStages[2]{
      {
          .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
          .pNext = nullptr,
          .flags = 0,
          .stage = VK_SHADER_STAGE_VERTEX_BIT,
          .module = vertexShader,
          .pName = "main",
          .pSpecializationInfo = nullptr,
      },
      {
          .sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
          .pNext = nullptr,
          .flags = 0,
          .stage = VK_SHADER_STAGE_FRAGMENT_BIT,
          .module = fragmentShader,
          .pName = "main",
          .pSpecializationInfo = nullptr,
      }};

  VkViewport viewports {
      .x = 0,
      .y = 0,
      .width = (float)swapchain.displaySize_.width,
      .height = (float)swapchain.displaySize_.height,
      .minDepth = 0.0f,
      .maxDepth = 1.0f,
  };

  VkRect2D scissor = {
          .offset = {.x = 0, .y = 0,},
          .extent = swapchain.displaySize_,
   };
  // Specify viewport info
  VkPipelineViewportStateCreateInfo viewportInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
      .pNext = nullptr,
      .viewportCount = 1,
      .pViewports = &viewports,
      .scissorCount = 1,
      .pScissors = &scissor,
  };

  // Specify multisample info
  VkSampleMask sampleMask = ~0u;
  VkPipelineMultisampleStateCreateInfo multisampleInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
      .pNext = nullptr,
      .rasterizationSamples = VK_SAMPLE_COUNT_1_BIT,
      .sampleShadingEnable = VK_FALSE,
      .minSampleShading = 0,
      .pSampleMask = &sampleMask,
      .alphaToCoverageEnable = VK_FALSE,
      .alphaToOneEnable = VK_FALSE,
  };

  // Specify color blend state
  VkPipelineColorBlendAttachmentState attachmentStates{
     .blendEnable = VK_FALSE,
     .colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT |
                       VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT,
  };
  VkPipelineColorBlendStateCreateInfo colorBlendInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
      .logicOpEnable = VK_FALSE,
      .logicOp = VK_LOGIC_OP_COPY,
      .attachmentCount = 1,
      .pAttachments = &attachmentStates,
  };

  // Specify rasterizer info
  VkPipelineRasterizationStateCreateInfo rasterInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
      .pNext = nullptr,
      .depthClampEnable = VK_FALSE,
      .rasterizerDiscardEnable = VK_FALSE,
      .polygonMode = VK_POLYGON_MODE_FILL,
      .cullMode = VK_CULL_MODE_NONE,
      .frontFace = VK_FRONT_FACE_CLOCKWISE,
      .depthBiasEnable = VK_FALSE,
      .lineWidth = 1,
  };

  // Specify input assembler state
  VkPipelineInputAssemblyStateCreateInfo inputAssemblyInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
      .pNext = nullptr,
      .topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
      .primitiveRestartEnable = VK_FALSE,
  };

  // Specify vertex input state
  VkVertexInputBindingDescription vertex_input_bindings{
      .binding = 0,
      .stride = 5 * sizeof(float),
      .inputRate = VK_VERTEX_INPUT_RATE_VERTEX,
  };
  VkVertexInputAttributeDescription vertex_input_attributes[2]{
      {
          .location = 0,
          .binding = 0,
          .format = VK_FORMAT_R32G32B32_SFLOAT,
          .offset = 0,
      },
      {
          .location = 1,
          .binding = 0,
          .format = VK_FORMAT_R32G32_SFLOAT,
          .offset = sizeof(float) * 3,
      }};
  VkPipelineVertexInputStateCreateInfo vertexInputInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
      .pNext = nullptr,
      .vertexBindingDescriptionCount = 1,
      .pVertexBindingDescriptions = &vertex_input_bindings,
      .vertexAttributeDescriptionCount = 2,
      .pVertexAttributeDescriptions = vertex_input_attributes,
  };

  // Create the pipeline cache
  VkPipelineCacheCreateInfo pipelineCacheInfo{
      .sType = VK_STRUCTURE_TYPE_PIPELINE_CACHE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,  // reserved, must be 0
      .initialDataSize = 0,
      .pInitialData = nullptr,
  };

  CALL_VK(vkCreatePipelineCache(device.device_, &pipelineCacheInfo, nullptr,
                                &gfxPipeline.cache_));

  // Create the pipeline
  VkGraphicsPipelineCreateInfo pipelineCreateInfo{
      .sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
      .stageCount = 2,
      .pStages = shaderStages,
      .pVertexInputState = &vertexInputInfo,
      .pInputAssemblyState = &inputAssemblyInfo,
      .pTessellationState = nullptr,
      .pViewportState = &viewportInfo,
      .pRasterizationState = &rasterInfo,
      .pMultisampleState = &multisampleInfo,
      .pDepthStencilState = nullptr,
      .pColorBlendState = &colorBlendInfo,
      .pDynamicState = &dynamicStateInfo,
      .layout = gfxPipeline.layout_,
      .renderPass = render.renderPass_,
      .subpass = 0,
      .basePipelineHandle = VK_NULL_HANDLE,
      .basePipelineIndex = 0,
  };

  VkResult pipelineResult = vkCreateGraphicsPipelines(
      device.device_, gfxPipeline.cache_, 1, &pipelineCreateInfo, nullptr,
      &gfxPipeline.pipeline_);

  // We don't need the shaders anymore, we can release their memory
  vkDestroyShaderModule(device.device_, vertexShader, nullptr);
  vkDestroyShaderModule(device.device_, fragmentShader, nullptr);

  return pipelineResult;
}


// initialize descriptor set
VkResult CreateDescriptorSet(void) {
  const VkDescriptorPoolSize type_count = {
      .type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
      .descriptorCount = TUTORIAL_TEXTURE_COUNT,
  };
  const VkDescriptorPoolCreateInfo descriptor_pool = {
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
      .pNext = nullptr,
      .flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT,
      .maxSets = 1,
      .poolSizeCount = 1,
      .pPoolSizes = &type_count,
  };

  CALL_VK(vkCreateDescriptorPool(device.device_, &descriptor_pool, nullptr,
                                 &gfxPipeline.descPool_));

  VkDescriptorSetAllocateInfo alloc_info{
      .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
      .pNext = nullptr,
      .descriptorPool = gfxPipeline.descPool_,
      .descriptorSetCount = 1,
      .pSetLayouts = &gfxPipeline.dscLayout_};
  CALL_VK(vkAllocateDescriptorSets(device.device_, &alloc_info,
                                   &gfxPipeline.descSet_));

  VkDescriptorImageInfo texDsts[TUTORIAL_TEXTURE_COUNT];
  memset(texDsts, 0, sizeof(texDsts));
  for (int32_t idx = 0; idx < TUTORIAL_TEXTURE_COUNT; idx++) {
    texDsts[idx].sampler = textures[idx].sampler;
    texDsts[idx].imageView = textures[idx].view;
    texDsts[idx].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
  }

  VkWriteDescriptorSet writeDst{
      .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
      .pNext = nullptr,
      .dstSet = gfxPipeline.descSet_,
      .dstBinding = 0,
      .dstArrayElement = 0,
      .descriptorCount = TUTORIAL_TEXTURE_COUNT,
      .descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER,
      .pImageInfo = texDsts,
      .pBufferInfo = nullptr,
      .pTexelBufferView = nullptr};
  vkUpdateDescriptorSets(device.device_, 1, &writeDst, 0, nullptr);
  return VK_SUCCESS;
}

bool InitVulkanCore() {
  if (device.initialized_) {
      LOGI("Vulkan already initialized");
      return true;
  }
  
  if (!g_nativeWindow) {
      LOGE("Native window not available");
      return false;
  }
  
  VkApplicationInfo appInfo = {
      .sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
      .pNext = nullptr,
      .pApplicationName = "vulkan_test_app_Harmony",
      .applicationVersion = VK_MAKE_VERSION(1, 0, 0),
      .pEngineName = "tutorial",
      .engineVersion = VK_MAKE_VERSION(1, 0, 0),
      .apiVersion = VK_API_VERSION_1_1,
  };
  
  LOGI("Window size: %lu x %lu", (unsigned long)g_windowWidth, (unsigned long)g_windowHeight);
  
  // create a device
  CreateVulkanDevice(g_nativeWindow, &appInfo);

  CreateSwapChain();

  // -----------------------------------------------------------------
  // Create render pass
  VkAttachmentDescription attachmentDescriptions{
      .format = swapchain.displayFormat_,
      .samples = VK_SAMPLE_COUNT_1_BIT,
      .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
      .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
      .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
      .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
      .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
      .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
  };

  VkAttachmentReference colourReference = {
      .attachment = 0, .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL};

  VkSubpassDescription subpassDescription{
      .flags = 0,
      .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
      .inputAttachmentCount = 0,
      .pInputAttachments = nullptr,
      .colorAttachmentCount = 1,
      .pColorAttachments = &colourReference,
      .pResolveAttachments = nullptr,
      .pDepthStencilAttachment = nullptr,
      .preserveAttachmentCount = 0,
      .pPreserveAttachments = nullptr,
  };
  VkRenderPassCreateInfo renderPassCreateInfo{
      .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
      .pNext = nullptr,
      .attachmentCount = 1,
      .pAttachments = &attachmentDescriptions,
      .subpassCount = 1,
      .pSubpasses = &subpassDescription,
      .dependencyCount = 0,
      .pDependencies = nullptr,
  };
  CALL_VK(vkCreateRenderPass(device.device_, &renderPassCreateInfo, nullptr,
                              &render.renderPass_));

  CreateFrameBuffers(render.renderPass_);
  CreateTexture();
  CreateBuffers();

  // Create graphics pipeline
  CreateGraphicsPipeline();

  CreateDescriptorSet();

  // -----------------------------------------------
  // Create a pool of command buffers to allocate command buffer from
  VkCommandPoolCreateInfo cmdPoolCreateInfo{
      .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
      .pNext = nullptr,
      .flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
      .queueFamilyIndex = device.queueFamilyIndex_,
  };
  CALL_VK(vkCreateCommandPool(device.device_, &cmdPoolCreateInfo, nullptr,
                              &render.cmdPool_));

  // Record a command buffer that just clear the screen
  // 1 command buffer draw in 1 framebuffer
  render.cmdBufferLen_ = swapchain.swapchainLength_;
  render.cmdBuffer_ = new VkCommandBuffer[swapchain.swapchainLength_];
  VkCommandBufferAllocateInfo cmdBufferCreateInfo{
      .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
      .pNext = nullptr,
      .commandPool = render.cmdPool_,
      .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
      .commandBufferCount = render.cmdBufferLen_,
  };
  CALL_VK(vkAllocateCommandBuffers(device.device_, &cmdBufferCreateInfo,
                                  render.cmdBuffer_));

  for (uint32_t bufferIndex = 0; bufferIndex < swapchain.swapchainLength_;
      bufferIndex++) {
      // We start by creating and declare the "beginning" our command buffer
      VkCommandBufferBeginInfo cmdBufferBeginInfo{
          .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
          .pNext = nullptr,
          .flags = 0,
          .pInheritanceInfo = nullptr,
      };
      CALL_VK(vkBeginCommandBuffer(render.cmdBuffer_[bufferIndex],
                                  &cmdBufferBeginInfo));

      // transition the buffer into color attachment
      setImageLayout(render.cmdBuffer_[bufferIndex],
                  swapchain.displayImages_[bufferIndex],
                  VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
                  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
                  VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
                  VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT);

      // Now we start a renderpass. Any draw command has to be recorded in a
      // renderpass
      VkClearValue clearVals{
          .color { .float32 { 0.0f, 0.34f, 0.90f, 1.0f,}},
      };

      VkRenderPassBeginInfo renderPassBeginInfo{
          .sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO,
          .pNext = nullptr,
          .renderPass = render.renderPass_,
          .framebuffer = swapchain.framebuffers_[bufferIndex],
          .renderArea = {.offset =
                          {
                              .x = 0, .y = 0,
                          },
                      .extent = swapchain.displaySize_},
          .clearValueCount = 1,
          .pClearValues = &clearVals};
      vkCmdBeginRenderPass(render.cmdBuffer_[bufferIndex], &renderPassBeginInfo,
                          VK_SUBPASS_CONTENTS_INLINE);
      // Bind what is necessary to the command buffer
      vkCmdBindPipeline(render.cmdBuffer_[bufferIndex],
                      VK_PIPELINE_BIND_POINT_GRAPHICS, gfxPipeline.pipeline_);
      vkCmdBindDescriptorSets(
          render.cmdBuffer_[bufferIndex], VK_PIPELINE_BIND_POINT_GRAPHICS,
          gfxPipeline.layout_, 0, 1, &gfxPipeline.descSet_, 0, nullptr);
      VkDeviceSize offset = 0;
      vkCmdBindVertexBuffers(render.cmdBuffer_[bufferIndex], 0, 1,
                          &buffers.vertexBuf_, &offset);

      // Draw Triangle
      vkCmdDraw(render.cmdBuffer_[bufferIndex], 3, 1, 0, 0);

      vkCmdEndRenderPass(render.cmdBuffer_[bufferIndex]);
      setImageLayout(render.cmdBuffer_[bufferIndex],
                  swapchain.displayImages_[bufferIndex],
                  VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
                  VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
                  VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
                  VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
      CALL_VK(vkEndCommandBuffer(render.cmdBuffer_[bufferIndex]));
  }

  // We need to create a fence to be able, in the main loop, to wait for our
  // draw command(s) to finish before swapping the framebuffers
  VkFenceCreateInfo fenceCreateInfo{
      .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
  };
  CALL_VK(
      vkCreateFence(device.device_, &fenceCreateInfo, nullptr, &render.fence_));

  // We need to create a semaphore to be able to wait, in the main loop, for our
  // framebuffer to be available for us before drawing.
  VkSemaphoreCreateInfo semaphoreCreateInfo{
      .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
      .pNext = nullptr,
      .flags = 0,
  };
  CALL_VK(vkCreateSemaphore(device.device_, &semaphoreCreateInfo, nullptr,
                          &render.semaphore_));

  device.initialized_ = true;
  LOGI("Vulkan initialization completed successfully");
  return true;
}
// InitVulkan:
//   Initialize Vulkan Context when XComponent window is created
//   upon return, vulkan is ready to draw frames

bool InitVulkan(OH_NativeXComponent* component) {
  LOGI("InitVulkan called with component: %p", component);
  
  nativeXComponent = component;

  // 初始化结构体
  memset(&device, 0, sizeof(device));
  memset(&swapchain, 0, sizeof(swapchain));
  memset(&buffers, 0, sizeof(buffers));
  memset(&gfxPipeline, 0, sizeof(gfxPipeline));
  memset(&render, 0, sizeof(render));

  // 修复：改进资源管理器初始化
  if (!resourceManager) {
      // void* userData = nullptr;
      // OH_NativeXComponent_GetUserData(component, &userData);
      
      // 直接尝试初始化资源管理器
      resourceManager = OH_ResourceManager_InitNativeResourceManager(nullptr, nullptr);
      
      if (!resourceManager) {
          LOGW("Failed to initialize resource manager, continuing without it");
          LOGI("Continuing without resource manager - textures will be skipped");
          // 不要返回 false，继续初始化其他部分
      } else {
          LOGI("Resource manager initialized successfully");
      }
  }
  
  
  
  LOGI("Vulkan base initialization completed, waiting for surface");
  
  // 如果已经有窗口，直接初始化核心部分
  if (g_nativeWindow && g_surfaceReady) {
      LOGI("Window and surface available, initializing Vulkan core immediately");
      return InitVulkanCore();
  }
  
  LOGI("Waiting for surface creation callback");
  return true;  // 基础初始化成功，等待surface创建
}




// IsVulkanReady():
//    native app poll to see if we are ready to draw...
bool IsVulkanReady(void) { 
  return device.initialized_ && g_surfaceReady && g_nativeWindow != nullptr; 
}

// Draw one frame
bool VulkanDrawFrame(void) {
  static float angle = 0.0f;  // 旋转角度
  angle += 0.01f;  // 每帧增加角度

  // 更新顶点数据（旋转三角形）
  float cosTheta = cos(angle);
  float sinTheta = sin(angle);
  const float vertexData[] = {
      -0.5f * cosTheta - -0.5f * sinTheta, -0.5f * sinTheta + -0.5f * cosTheta, 0.0f, 0.0f, 0.0f,
       0.5f * cosTheta - -0.5f * sinTheta, -0.5f * sinTheta +  0.5f * cosTheta, 0.0f, 1.0f, 0.0f,
       0.0f * cosTheta -  0.5f * sinTheta,  0.5f * sinTheta +  0.0f * cosTheta, 0.0f, 0.5f, 1.0f,
  };
  memcpy(buffers.mappedData_, vertexData, sizeof(vertexData));

  // 每次更新后 flush
  VkMappedMemoryRange flushRange{
      .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
      .pNext = nullptr,
      .memory = buffers.memory_,
      .offset = 0,
      .size = sizeof(vertexData)
  };
  CALL_VK(vkFlushMappedMemoryRanges(device.device_, 1, &flushRange));


  uint32_t nextIndex;
  CALL_VK(vkAcquireNextImageKHR(device.device_, swapchain.swapchain_,
                                UINT64_MAX, render.semaphore_, VK_NULL_HANDLE,
                                &nextIndex));
  CALL_VK(vkResetFences(device.device_, 1, &render.fence_));

  VkPipelineStageFlags waitStageMask =
      VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
  VkSubmitInfo submit_info = {.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
                              .pNext = nullptr,
                              .waitSemaphoreCount = 1,
                              .pWaitSemaphores = &render.semaphore_,
                              .pWaitDstStageMask = &waitStageMask,
                              .commandBufferCount = 1,
                              .pCommandBuffers = &render.cmdBuffer_[nextIndex],
                              .signalSemaphoreCount = 0,
                              .pSignalSemaphores = nullptr};
  CALL_VK(vkQueueSubmit(device.queue_, 1, &submit_info, render.fence_));
  CALL_VK(
      vkWaitForFences(device.device_, 1, &render.fence_, VK_TRUE, 100000000));

  VkResult result;
  VkPresentInfoKHR presentInfo{
      .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR,
      .pNext = nullptr,
      .waitSemaphoreCount = 0,
      .pWaitSemaphores = nullptr,
      .swapchainCount = 1,
      .pSwapchains = &swapchain.swapchain_,
      .pImageIndices = &nextIndex,
      .pResults = &result,
  };
  vkQueuePresentKHR(device.queue_, &presentInfo);
  return true;
}

void setImageLayout(VkCommandBuffer cmdBuffer, VkImage image,
                    VkImageLayout oldImageLayout, VkImageLayout newImageLayout,
                    VkPipelineStageFlags srcStages,
                    VkPipelineStageFlags destStages) {
  VkImageMemoryBarrier imageMemoryBarrier = {
      .sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
      .pNext = NULL,
      .srcAccessMask = 0,
      .dstAccessMask = 0,
      .oldLayout = oldImageLayout,
      .newLayout = newImageLayout,
      .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
      .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
      .image = image,
      .subresourceRange =
          {
              .aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
              .baseMipLevel = 0,
              .levelCount = 1,
              .baseArrayLayer = 0,
              .layerCount = 1,
          },
  };

  switch (oldImageLayout) {
    case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
      imageMemoryBarrier.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
      break;

    case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
      imageMemoryBarrier.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
      break;

    case VK_IMAGE_LAYOUT_PREINITIALIZED:
      imageMemoryBarrier.srcAccessMask = VK_ACCESS_HOST_WRITE_BIT;
      break;

    default:
      break;
  }

  switch (newImageLayout) {
    case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
      imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;
      break;

    case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
      imageMemoryBarrier.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
      break;

    case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
      imageMemoryBarrier.dstAccessMask = VK_ACCESS_SHADER_READ_BIT;
      break;

    case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
      imageMemoryBarrier.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
      break;

    case VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL:
      imageMemoryBarrier.dstAccessMask =
          VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
      break;

    default:
      break;
  }

  vkCmdPipelineBarrier(cmdBuffer, srcStages, destStages, 0, 0, NULL, 0, NULL, 1,
                       &imageMemoryBarrier);
}

void DeleteSwapChain(void) {
  if (swapchain.framebuffers_) {
    for (uint32_t i = 0; i < swapchain.swapchainLength_; i++) {
      if (swapchain.framebuffers_[i] != VK_NULL_HANDLE) {
        vkDestroyFramebuffer(device.device_, swapchain.framebuffers_[i], nullptr);
      }
    }
    delete[] swapchain.framebuffers_;
    swapchain.framebuffers_ = nullptr;
  }
  
  if (swapchain.displayViews_) {
    for (uint32_t i = 0; i < swapchain.swapchainLength_; i++) {
      if (swapchain.displayViews_[i] != VK_NULL_HANDLE) {
        vkDestroyImageView(device.device_, swapchain.displayViews_[i], nullptr);
      }
    }
    delete[] swapchain.displayViews_;
    swapchain.displayViews_ = nullptr;
  }
  
  if (swapchain.displayImages_) {
    delete[] swapchain.displayImages_;
    swapchain.displayImages_ = nullptr;
  }

  if (swapchain.swapchain_ != VK_NULL_HANDLE) {
    vkDestroySwapchainKHR(device.device_, swapchain.swapchain_, nullptr);
    swapchain.swapchain_ = VK_NULL_HANDLE;
  }
}

void DeleteGraphicsPipeline(void) {
  if (gfxPipeline.pipeline_ != VK_NULL_HANDLE) {
    vkDestroyPipeline(device.device_, gfxPipeline.pipeline_, nullptr);
    gfxPipeline.pipeline_ = VK_NULL_HANDLE;
  }
  if (gfxPipeline.cache_ != VK_NULL_HANDLE) {
    vkDestroyPipelineCache(device.device_, gfxPipeline.cache_, nullptr);
    gfxPipeline.cache_ = VK_NULL_HANDLE;
  }
  if (gfxPipeline.descSet_ != VK_NULL_HANDLE && gfxPipeline.descPool_ != VK_NULL_HANDLE) {
    vkFreeDescriptorSets(device.device_, gfxPipeline.descPool_, 1, &gfxPipeline.descSet_);
    gfxPipeline.descSet_ = VK_NULL_HANDLE;
  }
  if (gfxPipeline.descPool_ != VK_NULL_HANDLE) {
    vkDestroyDescriptorPool(device.device_, gfxPipeline.descPool_, nullptr);
    gfxPipeline.descPool_ = VK_NULL_HANDLE;
  }
  if (gfxPipeline.dscLayout_ != VK_NULL_HANDLE) {
    vkDestroyDescriptorSetLayout(device.device_, gfxPipeline.dscLayout_, nullptr);
    gfxPipeline.dscLayout_ = VK_NULL_HANDLE;
  }
  if (gfxPipeline.layout_ != VK_NULL_HANDLE) {
    vkDestroyPipelineLayout(device.device_, gfxPipeline.layout_, nullptr);
    gfxPipeline.layout_ = VK_NULL_HANDLE;
  }
}

void DeleteTexture(void) {
  for (uint32_t i = 0; i < TUTORIAL_TEXTURE_COUNT; i++) {
    if (textures[i].view != VK_NULL_HANDLE) {
      vkDestroyImageView(device.device_, textures[i].view, nullptr);
      textures[i].view = VK_NULL_HANDLE;
    }
    if (textures[i].sampler != VK_NULL_HANDLE) {
      vkDestroySampler(device.device_, textures[i].sampler, nullptr);
      textures[i].sampler = VK_NULL_HANDLE;
    }
    if (textures[i].image != VK_NULL_HANDLE) {
      vkDestroyImage(device.device_, textures[i].image, nullptr);
      textures[i].image = VK_NULL_HANDLE;
    }
    if (textures[i].mem != VK_NULL_HANDLE) {
      vkFreeMemory(device.device_, textures[i].mem, nullptr);
      textures[i].mem = VK_NULL_HANDLE;
    }
  }
}

void DeleteVulkan() {
  // 等待设备空闲
  if (device.device_ != VK_NULL_HANDLE) {
    vkDeviceWaitIdle(device.device_);
  }

  // 清理同步对象
  if (render.fence_ != VK_NULL_HANDLE) {
    vkDestroyFence(device.device_, render.fence_, nullptr);
    render.fence_ = VK_NULL_HANDLE;
  }
  if (render.semaphore_ != VK_NULL_HANDLE) {
    vkDestroySemaphore(device.device_, render.semaphore_, nullptr);
    render.semaphore_ = VK_NULL_HANDLE;
  }

  // 清理命令缓冲区
  if (render.cmdBuffer_) {
    vkFreeCommandBuffers(device.device_, render.cmdPool_, render.cmdBufferLen_,
                         render.cmdBuffer_);
    delete[] render.cmdBuffer_;
    render.cmdBuffer_ = nullptr;
  }

  if (render.cmdPool_ != VK_NULL_HANDLE) {
    vkDestroyCommandPool(device.device_, render.cmdPool_, nullptr);
    render.cmdPool_ = VK_NULL_HANDLE;
  }

  if (render.renderPass_ != VK_NULL_HANDLE) {
    vkDestroyRenderPass(device.device_, render.renderPass_, nullptr);
    render.renderPass_ = VK_NULL_HANDLE;
  }
  
  // 添加纹理清理
  DeleteTexture();
  
  DeleteSwapChain();
  DeleteGraphicsPipeline();
  DeleteBuffers();

  if (device.surface_ != VK_NULL_HANDLE) {
    vkDestroySurfaceKHR(device.instance_, device.surface_, nullptr);
    device.surface_ = VK_NULL_HANDLE;
  }

  if (device.device_ != VK_NULL_HANDLE) {
    vkDestroyDevice(device.device_, nullptr);
    device.device_ = VK_NULL_HANDLE;
  }

  if (device.instance_ != VK_NULL_HANDLE) {
    vkDestroyInstance(device.instance_, nullptr);
    device.instance_ = VK_NULL_HANDLE;
  }
  

  if (resourceManager) {
      OH_ResourceManager_ReleaseNativeResourceManager(resourceManager);
      resourceManager = nullptr;
  }
    
  device.initialized_ = false;
}


